{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# CUDA语义\n",
    "torch.cuda 会记录当前选择的GPU，并且分配的所有CUDA张量将在上面创建。可以使用 torch.cuda.device 上下文管理器更改所选设备。\n",
    "\n",
    "但是，一旦张量被分配，您可以直接对其进行操作，而不考虑所选择的设备，结果将始终放 在与张量相同的设备上。\n",
    "\n",
    "默认情况下，不支持跨GPU操作，唯一的例外是 copy_() 。 除非启用对等存储器访问，否则 对分布不同设备上的张量任何启动操作的尝试都将会引发错误。\n",
    "下面你可以找到一个展示如下的小例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([1.2096])\n",
      "1.209649682044983\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "x = torch.randn(1)\n",
    "print(x)\n",
    "print(x.item())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([2.2096], device='cuda:0')\n",
      "tensor([2.2096], dtype=torch.float64)\n"
     ]
    }
   ],
   "source": [
    "# is_available 函数判断是否有cuda可以使用\n",
    "# ``torch.device``将张量移动到指定的设备中\n",
    "if torch.cuda.is_available():\n",
    "    device = torch.device(\"cuda\")          # a CUDA 设备对象\n",
    "    y = torch.ones_like(x, device=device)  # 直接从GPU创建张量\n",
    "    x = x.to(device)                       # 或者直接使用``.to(\"cuda\")``将张量移动到cuda中\n",
    "    z = x + y\n",
    "    print(z)\n",
    "    print(z.to(\"cpu\", torch.double))       # ``.to`` 也会对变量的类型做更改"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 最佳实践 \n",
    "使用固定的内存缓冲区\n",
    "当副本来自固定（页锁）内存时，主机到GPU的复制速度要快很多。\n",
    "\n",
    "CPU张量和存储开放了 一个 pin_memory() 方法，它返回该对象的副本，而它的数据放在固定区域中。\n",
    "\n",
    "另外，一旦固定了张量或存储，就可以使用异步的GPU副本。只需传递一个额外的 async=True 参数到 cuda() 的调用。\n",
    "\n",
    "这可以用于将数据传输与计算重叠。通过将 pin_memory=True 传递给其构造函数，可以使 DataLoader 将batch返回到固定内存中。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用 nn.DataParallel 替代 multiprocessing CUDA语义\n",
    "\n",
    "大多数涉及批量输入和多个GPU的情况应默认使用 DataParallel 来使用多个GPU。尽管有GIL的存在，单个python进程也可能使多个GPU饱和。\n",
    "\n",
    "从0.1.9版本开始，大量的GPU(8+)可能未被充分利用。然而，这是一个已知的问题，也正在积极开发。和往常一样，测试你的用例吧。\n",
    "\n",
    "调用 multiprocessing 来利用CUDA模型存在重要的注意事项；使用具有多处理功能的CUDA模型有重要的注意事项；除非就是需要谨慎地满足数据处理需求，否则您的程序很可能会出现错误或未定义的行为。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
